#ifndef _I_BUFFER_H_
#define _I_BUFFER_H_

#include "../cuCommon/cuTypes.h"

#include "../interface/I_api_Buffer.h"

enum e_Map
{
	e_Map_RO = 1,    // read only
	e_Map_WO = 2,    // write only
	e_Map_RW = 3,    // read write
	e_Map_Lock = 4,  // map buffer object resource to compute pointer 
};

enum e_Copy
{
	e_Copy_DtoH = 1, // device to host
	e_Copy_HtoD = 2, // host to device
};

enum e_Buffer
{
	e_Buffer_R = 1,  // read buffer
	e_Buffer_W = 2,  // write buffer
	e_Buffer_RW = 3, // both buffers
};

//////////////////////////////////////////////////////////////////////////
/// forward declaration of interface in this header
//////////////////////////////////////////////////////////////////////////
class I_Buffer;
class I_Buffer2D;
class I_DoubleBuffer;

class I_api_Device;

template <class T> class T_Buffer_CU;
template <class T> class T_Buffer2D_CU;
template <class T> class T_DoubleBuffer_CU;

//////////////////////////////////////////////////////////////////////////
/// I_Buffer is an interface for handling memory as a 1D arrays
/// on cpu and cuda / d3d / ogl devices
//////////////////////////////////////////////////////////////////////////
class I_Buffer
{
protected:
	int				m_flag;
	int				m_size;

	int       m_offset;
	bool			m_owner;

	friend class I_Buffer2D;
public:
	I_Buffer(void) 
	{ 
		m_flag = m_size = m_offset = 0;  
		m_owner = false; 
	}
	virtual ~I_Buffer(void) {}

	//////////////////////////////////////////////////////////////////////////
	/// I_Buffer knows how to create and release its pointers
	//////////////////////////////////////////////////////////////////////////
	virtual void        Create(int nSize, I_api_Device *pDevice = NULL)=0;
	virtual void        Create(int nSize, I_Buffer * pShared)=0;
	virtual bool        Request(int nSize, int * pOffset, void **hPtr, void **dPtr, void ** bPtr)=0;
	virtual void				Release(void) = 0;

	//////////////////////////////////////////////////////////////////////////
	/// I_Buffer knows how to map and unmap memory into one another
	/// Mapping parameters for the buffer interface
	/// Use cases
	///  When you need to write initial values to cuda pointer
	///    Call Map(WO)
	///    Map instantly returns host pointer
	///    Write into host pointer
	///    Call Unmap ()
	///    Unmap checks that flag was WO and writes back to cuda array
	///  When you need to get data from cuda pointer
	///    Call Map(RO)
	///    Map copies data from cuda pointer to host and returns host pointer
	///    Call Unmap()
	///    Unmap() doesn't change anything
	///  When you need to Read cuda pointer Modify and Write
	///    Call Map(RW)
	///    Map copies data from cuda pointer to host and returns host pointer
	///    Read host pointer 
	///    Call Unmap()
	///    Unmap() copies data from host pointer to cuda pointer
	///  In some cases what you need is an OGL or D3D buffer and you want to 
	///  map pointers to CUDA. the way you do it is 
	///    Call Map(BO)
	///    Map return device pointer
	///    Call Unmap
	///    Unmap does unmapping if necessary
	///    If you chose to use Map to get device pointer you need to call unmap
	//////////////////////////////////////////////////////////////////////////
	virtual void			*	Map(int32 nFlag) = 0;
	virtual void				Unmap(void) = 0;

	//////////////////////////////////////////////////////////////////////////
	/// I_Buffer knows how to copy data back and forth
	/// I_Buffer allows to some way to exchange data between pointers
	/// without any notion of what those pointers really are
	/// Copy direction DtoH copies from start index to stop device to host
	/// Copy direction HtoD copies from start index to stop host   to device
	//////////////////////////////////////////////////////////////////////////
	virtual void				Memcpy(int32 nDir, int32 nStart = 0, int32 nCount = 0) = 0;
	virtual void				Memset(void *pElem, int32 nStart = 0, int32 nCount = 0) = 0;

	//////////////////////////////////////////////////////////////////////////
	/// I_Buffer has host and device pointers that it can return
	//////////////////////////////////////////////////////////////////////////
	virtual void			* hPtr(void)=0;
	virtual void        hPtr(void *ptr) {}

	virtual void			* dPtr(void)=0;
	virtual void        dPtr(void *ptr) {}

	virtual I_api_Buffer	* bufferPtr(void)=0;
	virtual void						bufferPtr(I_api_Buffer * ptr) {}

	//////////////////////////////////////////////////////////////////////////
	/// Size returns size in elements wheres SizeInBytes is in bytes
	//////////////////////////////////////////////////////////////////////////
	virtual int					Size(void) { return m_size; }
	virtual void				Size(int size) { m_size = size; }

	virtual int					SizeInBytes(void)=0;

	//////////////////////////////////////////////////////////////////////////
	/// I_Buffer also gives access to all other protected fields
	//////////////////////////////////////////////////////////////////////////
	virtual int         Offset(void) { return m_offset; }
	virtual void        Offset(int offset) { m_offset = offset; }

	virtual int         Flag(){ return m_flag; }

	virtual bool        Owner() { return m_owner; }
	virtual void        Owner(bool owner) { m_owner = owner; }
};

#endif // _I_BUFFER_H_